#ifndef GAME_ANIMATION_SKY_SHADER_H
#define GAME_ANIMATION_SKY_SHADER_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#include <OpenTissue/gpu/cg/cg_program.h>
#include <OpenTissue/gpu/image/image.h>
#include <OpenTissue/gpu/image/io/image_read.h>
#include <OpenTissue/gpu/image/io/image_write.h>
#include <OpenTissue/gpu/texture/texture_texture2D.h>
#include <OpenTissue/gpu/image/image_make_noise.h>
#include <OpenTissue/gpu/image/image_alpha_modulation.h>

namespace animation
{

  /**
   * The Sky Shader Class.
   * Sets up rendering before drawing sky dome geometry.
   */
  class SkyShader
  {
  protected:

    OpenTissue::cg::Program                                      m_vertex_program;
    OpenTissue::cg::Program                                      m_fragment_program;
    boost::shared_ptr<OpenTissue::image::Image<unsigned char> >  m_star_image;
    OpenTissue::texture::texture2D_pointer                       m_star_texture;
  public:
	  float s_curve(float t,float from,float to){
		  t=(t-from)/(to-from);
		  return t*t*t*(10+t*(6*t-15));
	  }
	  float exp_curve(float t,float from,float to,float a){
		  return pow((t-from)/(to-from),a);
	  }


	  float lerp(float from,float to,float at){
		  return from+at*(to-from);
	  }

    bool pre_render (
		bool reload
		, float time
	  ,  float daytime
	  ,float hour_length
	  , float fog_density
	  , float fog_color_r, float fog_color_g, float fog_color_b
	  , bool flipped
      ){
        //--- Setup openGL state
		if(flipped==false){
          glCullFace(GL_FRONT);
          glEnable(GL_CULL_FACE);
		}
        glDepthMask(0);
		if(!m_star_image){
			m_star_image.reset( new OpenTissue::image::Image<unsigned char>() );
			OpenTissue::image::read(demo_path + "TheSwampThing2008/data/stars.png",*m_star_image);
			m_star_texture = m_star_image->create_texture(GL_RGBA);
			m_star_texture->set_repeating(true);
			m_star_texture->set_mipmapping(true);
		}

      //--- Load vertex program, note that cg-program path is currently hardcoded, a
      //--- more sensible solution would be
      //---
      //---  std::string filename = get_environment_variable("OPENTISSUE") + .... + "vp_grass.cg";
      //---
	  bool no_shade=false;
      if(!m_vertex_program.is_program_loaded()||reload)
		  if(!m_vertex_program.load_from_file(OpenTissue::cg::Program::vertex_program,demo_path + "../animation/vp_sky.cg")){
			  return false;
		  }

	  if(!m_fragment_program.is_program_loaded()||reload){
		if(!m_fragment_program.load_from_file(OpenTissue::cg::Program::fragment_program,demo_path + "../animation/fp_sky.cg")){
			return false;
	 	}
	  }
      float zenit[3];
	  float horizon[3];
      //day;
	  float blue[3];
	  blue[0]=122.0/255.0;
	  blue[1]=182.0/255.0;
	  blue[2]=232.0/255.0;
	  float white[3];
	  white[0]=1.0;
	  white[1]=1.0;
	  white[2]=1.0;

	  float dark[3];
	  dark[0]=29.0/255.0;
	  dark[1]=32.0/255.0;
	  dark[2]=39.0/255.0;

	  float ldark[3];
	  ldark[0]=29.0/255.0+0.05;
	  ldark[1]=32.0/255.0+0.05;
	  ldark[2]=39.0/255.0+0.05;

	  float red[3];
	  red[0]=196.0/255.0;
	  red[1]=13.0/255.0;
	  red[2]=13.0/255.0;

	  float periods[4];
	  periods[0]=5.0*hour_length;//<nite
	  periods[1]=6.0*hour_length;//<sunrise
	  periods[2]=7.0*hour_length;//<sunrise
	  periods[3]=17.0*hour_length;//<day
	  periods[4]=18.0*hour_length;//<day
	  periods[5]=19.0*hour_length;//<sunset
	  //periods[2]=5.5*hour_length;//the end of sun set and rise nite
      float star_filter=0.0;
	  float a=0.224;
	  if(daytime<periods[0]||daytime>periods[5]){
		  horizon[0]=ldark[0]; 
		  horizon[1]=ldark[1]; 
		  horizon[2]=ldark[2]; 
		  zenit[0]=dark[0];
		  zenit[1]=dark[1];
		  zenit[2]=dark[2];
		  star_filter=1.0;
	  }else{
		  if(daytime<periods[1]){
			  float s=s_curve(daytime,periods[0],periods[1]);
			  horizon[0]=lerp(ldark[0],red[0],s);
			  horizon[1]=lerp(ldark[1],red[1],s);
			  horizon[2]=lerp(ldark[2],red[2],s);
			  
			  s=s_curve(daytime,periods[0],periods[2]);
			  zenit[0]=lerp(dark[0],blue[0],s);
			  zenit[1]=lerp(dark[1],blue[1],s);
			  zenit[2]=lerp(dark[2],blue[2],s);
			  star_filter=1.0-s;
		  }else{
			  if(daytime<periods[2]){
				  float s=s_curve(daytime,periods[1],periods[2]);
				  horizon[0]=lerp(red[0],white[0],s);
				  horizon[1]=lerp(red[1],white[1],s);
				  horizon[2]=lerp(red[2],white[2],s);
				  s=s_curve(daytime,periods[0],periods[2]);
				  zenit[0]=lerp(dark[0],blue[0],s);
				  zenit[1]=lerp(dark[1],blue[1],s);
				  zenit[2]=lerp(dark[2],blue[2],s);
			  }else{
				  if(daytime<periods[3]){
					  horizon[0]=white[0];
					  horizon[1]=white[1];
					  horizon[2]=white[2];
					  zenit[0]=blue[0];
					  zenit[1]=blue[1];
					  zenit[2]=blue[2];
				  }else{
					  if(daytime<periods[4]){
						  float s=s_curve(daytime,periods[3],periods[4]);
						  horizon[0]=lerp(white[0],red[0],s);
						  horizon[1]=lerp(white[1],red[1],s);
						  horizon[2]=lerp(white[2],red[2],s);

					  }else{
						  float s=s_curve(daytime,periods[4],periods[5]);
						  horizon[0]=lerp(red[0],ldark[0],s);
						  horizon[1]=lerp(red[1],ldark[1],s);
						  horizon[2]=lerp(red[2],ldark[2],s);
					  }
					  float s=s_curve(daytime,periods[3],periods[5]);
					  zenit[0]=lerp(blue[0],dark[0],s);
					  zenit[1]=lerp(blue[1],dark[1],s);
					  zenit[2]=lerp(blue[2],dark[2],s);
					  star_filter=s;
				  }
			  }
		  }
	  }
	  m_vertex_program.set_float_param("zenit",zenit[0],zenit[1],zenit[2]);
	  m_vertex_program.set_float_param("horizon",horizon[0],horizon[1],horizon[2]);
	  m_vertex_program.set_float_param("time",time);
	  
	  m_vertex_program.set_modelview_projection_matrix();
	  //--- load fragment program
	  m_fragment_program.set_float_param( "fog_density",  fog_density  );
	  m_fragment_program.set_float_param( "fog_color",  fog_color_r,fog_color_g,fog_color_b);
      m_fragment_program.set_input_texture("stars",m_star_texture);
	  m_fragment_program.set_float_param("star_filter",star_filter);
	  m_fragment_program.enable();
	  m_vertex_program.enable();
	  return true;
	}


    void post_render()
    {
      glDepthMask(1);
      glCullFace(GL_BACK);
      glDisable(GL_CULL_FACE);
      m_fragment_program.disable();
      m_vertex_program.disable();
    }
  };

} // namespace animation

// GAME_ANIMATION_SKY_SHADER_H
#endif
